﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Reflection;
using System.Diagnostics;

namespace CodeDomHelper.Types
{

    [DebuggerDisplay("{AssemblyQualifiedNameUltraShort}", Name = "TypeExpression")]
    //[System.Diagnostics.DebuggerStepThrough()]
    [Serializable()]
    public class BuildTypeExpression : IEnumerable<TypeExpression>
    {

        #region private
        
        private const string patternShort = @",?\sVersion=\d+\.\d+\.\d+\.\d+,?|\s,?Culture=\w+,?|\s,?PublicKeyToken=[\w\d]+,?";
        private const string patternUltraShort = @",?\sVersion=\d+\.\d+\.\d+\.\d+,?|\s,?Culture=\w+,?|\s,?PublicKeyToken=[\w\d]+,?|, mscorlib";
        protected LexicalAnalyzer _lexicalAnalyzer;
        internal protected List<TypeExpression> Liste;

        internal protected virtual void Parse()
        {
            _lexicalAnalyzer.MoveNext();
            ExpressionValue = GetExpression();
            ExpressionValue.Parse();
        }


        #endregion

        #region ctor
        
        [System.Diagnostics.DebuggerStepThrough()]
        private BuildTypeExpression()
        {
            this.Liste = new List<TypeExpression>();
        }

        protected BuildTypeExpression _rootExpression;
        [System.Diagnostics.DebuggerStepThrough()]
        public BuildTypeExpression(BuildTypeExpression rootExpression)
            : this(rootExpression._lexicalAnalyzer)
        {
            _rootExpression = rootExpression;
        }
        [System.Diagnostics.DebuggerStepThrough()]
        protected BuildTypeExpression(LexicalAnalyzer lexicalAnalyzer) : this()
        {
            // TODO: Complete member initialization
            this._lexicalAnalyzer = lexicalAnalyzer;            
        }

        #endregion

        #region static
        

        public static TypeExpression Analyse(string requete)
        {
            BuildTypeExpression e = new BuildTypeExpression(new LexicalAnalyzer(requete));
            e.Parse();
            return e.ExpressionValue;
        }


        public static TypeExpression Analyse(Type type)
        {
            return Analyse(type.AssemblyQualifiedName);
        }

        #endregion


        protected TypeExpression GetExpression()
        {

            TypeExpression ret = null;
            if (_lexicalAnalyzer.Generic == 0)
                ret = new TypeExpression(this) { Generic = _lexicalAnalyzer.Generic, TypeName = _lexicalAnalyzer.Value };
            
            else
                ret = new TypeGenericExpression(this) { Generic = _lexicalAnalyzer.Generic, TypeName = _lexicalAnalyzer.Value };

            if (_lexicalAnalyzer.IsArray)
                ret.IsArray = true;

            return ret;

        }


        #region Add Generic

        [System.Diagnostics.DebuggerStepThrough()]
        public void Add(TypeExpression item)
        {
            this.Liste.Add(item);
            Generic = this.Liste.Count;
        }

        public void Add(int position, TypeExpression item)
        {
            this.Liste.Insert(position, item);
            Generic = this.Liste.Count;
        }

        [System.Diagnostics.DebuggerStepThrough()]
        public void Add(string item)
        {
            var i = BuildTypeExpression.Analyse(item);
            this.Liste.Add(i);
            Generic = this.Liste.Count;
        }

        [System.Diagnostics.DebuggerStepThrough()]
        public void Add(int position, string item)
        {
            var i = BuildTypeExpression.Analyse(item);
            this.Liste.Insert(position, i);
            Generic = this.Liste.Count;
        }

        [System.Diagnostics.DebuggerStepThrough()]
        public void Add(Type item)
        {
            var i = BuildTypeExpression.Analyse(item.AssemblyQualifiedName);
            this.Liste.Add(i);
            Generic = this.Liste.Count;
        }

        public void Add(int position, Type item)
        {
            var i = BuildTypeExpression.Analyse(item.AssemblyQualifiedName);
            this.Liste.Insert(position, i);
            Generic = this.Liste.Count;
        }

        #endregion

        #region Properties

        public TypeExpression ExpressionValue { get; set; }

        public int Generic { get; private set; }        

        public string TypeName { get; set; }

        public string AssemblyName 
        {
            get
            {
                string[] tb = TypeName.Split(',');

                if (tb.Length >= 2)
                    return tb[1].Trim();

                return string.Empty;     
            }
        }        

        public string Name 
        {
            get
            {
                string[] tb = FullName.Split('.');
                return tb[tb.Length - 1];
            }
        }

        public bool IsCanGeneric { get { return Generic > 0; } }
        public bool IsGeneric { get { return Liste.Count > 0; } }
        public int CountGeneric { get { return Liste.Count; } }

        public string Namespace 
        {
            get
            {                
                string _fullName = FullName;
                string[] tb = _fullName.Split('.');
                string Name = tb[tb.Length - 1];
                if (tb.Length > 1)
                    return _fullName.Substring(0, _fullName.Length - Name.Length - 1);
                return String.Empty;
            }
        }


        public string FullName 
        {
            get
            {
                string ret = string.Empty;

                string[] tb = TypeName.Split(',');

                if (tb.Length >= 1)
                    ret = tb[0];

                if (IsArray)
                    ret += "[]";

                return ret;              

            }
                  
        }

        public TypeExpression SubType { get { return Liste.FirstOrDefault(); } }


        public string AssemblyQualifiedNameShort
        {
            get
            {
                string ret = AssemblyQualifiedName;
                Regex r = new Regex(patternShort, RegexOptions.Compiled);
                ret = r.Replace(ret, string.Empty);
                return ret;
            }
        }


        public string AssemblyQualifiedNameUltraShort
        {
            get
            {
                string ret = AssemblyQualifiedName;
                Regex r = new Regex(patternUltraShort, RegexOptions.Compiled);
                ret = r.Replace(ret, string.Empty);
                return ret;
            }
        }

 
        public string AssemblyQualifiedName
        {
            get
            {

                string ret = string.Empty;

                int i = TypeName.IndexOf(',');

                if (Generic == 0)
                {

                    if (i == -1)
                    {

                        ret = TypeName;

                        if (IsArray)
                            ret += "[]";

                    }
                    else
                    {

                        ret = TypeName.Substring(0, i);

                        if (IsArray)
                            ret += "[]";

                        ret += TypeName.Substring(i, TypeName.Length - i);

                    }

                    return ret;

                }


                if (i != -1)
                    ret = TypeName.Substring(0, i) + "[{0}]" + TypeName.Substring(i, TypeName.Length - i);
                else
                    ret = TypeName + "[{0}]";

                string s = string.Empty;
                string separator = string.Empty;

                if (this.Liste.Count >= 1)

                    foreach (BuildTypeExpression item in this.Liste)
                    {
                        s += separator + "[" + item.AssemblyQualifiedName + "]";

                        if (this.Liste.Count > 1)
                            separator = ",";
                    }


                return string.Format(ret, s).Replace("[]", "");

            }
        }


        public BuildTypeExpression this[int index] { get { return Liste[index]; } }

        public bool IsArray { get; internal set; }

        #endregion


        public Type Evaluate(Func<string, Type> ActionResolver)
        {

            Type t = ActionResolver(TypeName);

            if (t == null)
            {

                t = Type.GetType(TypeName);

                if (t == null)
                    return null;
            }

            if (t.IsGenericType)
            {
                List<Type> a = new List<Type>();

                foreach (TypeExpression item in Liste)
                    a.Add(item.Evaluate(ActionResolver));

                t = t.MakeGenericType(a.ToArray());
            }

            return t;
        }


        public Type Evaluate()
        {
            return Evaluate((e) => FindType(e));
        }


        private Type FindType(string typeName)
        {

            if (!typeName.Contains("["))
            {

                Type t = Type.GetType(typeName);
                
                if (t != null)
                    return t;
            }
            else
            {
                var e = (TypeExpression)typeName;
                return e.Evaluate(c => FindType(c));
            }

            return null;
        }


        public override string ToString()
        {
            return ToString(true);
        }

        public string ToString(bool longForm)
        {
            string f = longForm ? FullName : Name;

            if (IsCanGeneric)
            {
                int i = f.IndexOf("`");
                f = f.Substring(0, i) + "<";

                bool p = false;
                foreach (BuildTypeExpression t in Liste)
                {

                    if (p)
                        f += ", ";

                    f += t.ToString(longForm);

                    p = true;
                }

                f += ">";
            }

            return f;
        }



        public TypeExpression Translate(Type type) { return (TypeExpression)Translate(type.AssemblyQualifiedName); }

    
        public TypeExpression Translate(string type)
        {
            TypeExpression i = (TypeExpression)this.MemberwiseClone();
            i.TypeName = type;
            return i;
        }


        public TypeExpression Clone()
        {
            TypeExpression i = (TypeExpression)this.MemberwiseClone();
            return i;
        }

   
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.Liste.GetEnumerator();
        }

        public IEnumerator<TypeExpression> GetEnumerator()
        {
            return this.Liste.GetEnumerator();
        }

        public override bool Equals(object obj)
        {
            return ((obj.GetType() == this.GetType()) && (obj as TypeExpression).AssemblyQualifiedName == this.AssemblyQualifiedName);
        }

        public override int GetHashCode()
        {
            return AssemblyQualifiedName.GetHashCode();
        }


        public static explicit operator BuildTypeExpression(Type type)
        {
            return BuildTypeExpression.Analyse(type.AssemblyQualifiedName);
        }

    
        public static explicit operator BuildTypeExpression(string type)
        {
            return BuildTypeExpression.Analyse(type);
        }


    }

}

